/**********************************************************************************
**
** Copyright (C) 2009 Jostein Bratlie
** Contact: jostein.bratlie@gmail.com | http://bratlie.info | http://episteme.hin.no
**
** This file is part of GMQtExt, a GMlib and Qt extension library
**
** GMQtExt is free software: you can redistribute it and/or modify
** it under the terms of the GNU Lesser General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** GMQtExt is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public License
** along with GMQtExt.  If not, see <http://www.gnu.org/licenses/>.
**
**********************************************************************************/


// Qt
#include <QtGui>


// local
#include "editorfactory.h"

// GMlib
#include <gmPoint2D>



namespace GMQtExt {



  // Set a hard coded left margin to account for the indentation
  // of the tree view icon when switching to an editor

  static inline void setupTreeViewEditorMargin(QLayout *lt)
  {
      enum { DecorationMargin = 4 };
      if (QApplication::layoutDirection() == Qt::LeftToRight)
          lt->setContentsMargins(DecorationMargin, 0, 0, 0);
      else
          lt->setContentsMargins(0, 0, DecorationMargin, 0);
  }






  SurfacePlotDialog::SurfacePlotDialog( QWidget* parent ) : QDialog(parent ) {

    setWindowTitle( tr("Select Replot Settings") );
    QGridLayout *layout = new QGridLayout( this );

    QLabel *samp_label, *der_label, *u_label, *v_label;
    samp_label = new QLabel("Samples", this);
    der_label = new QLabel("Derivatives", this);
    u_label = new QLabel("u", this);
    v_label = new QLabel("v", this);

    _samp_u = new QSpinBox(this);
    _samp_v = new QSpinBox(this);
    _der_u = new QSpinBox(this);
    _der_v = new QSpinBox(this);

    layout->addWidget( u_label, 1, 0 );
    layout->addWidget( v_label, 2, 0 );

    layout->addWidget( samp_label, 0, 1 );
    layout->addWidget( _samp_u, 1, 1 );
    layout->addWidget( _samp_v, 2, 1 );

    layout->addWidget( der_label, 0, 2 );
    layout->addWidget( _der_u, 1, 2 );
    layout->addWidget( _der_v, 2, 2 );


    QDialogButtonBox *bb = new QDialogButtonBox( this );
    bb->addButton( QDialogButtonBox::Ok );
    bb->addButton( QDialogButtonBox::Cancel );
    connect( bb, SIGNAL(accepted()), this, SLOT(accept()) );
    connect( bb, SIGNAL(rejected()), this, SLOT(reject()) );
    layout->addWidget( bb, 3, 0, 1, 3 );
  }

  void SurfacePlotDialog::get( GMlib::Point<int,2>& no_samp, GMlib::Point<int,2>& no_der ) {

    no_samp = GMlib::Point2D<int>( _samp_u->value(), _samp_v->value() );
    no_der = GMlib::Point2D<int>( _der_u->value(), _der_v->value() );
  }

  void SurfacePlotDialog::set(const GMlib::Point<int, 2> &no_samp, const GMlib::Point<int, 2> &no_der) {

    _samp_u->setValue( no_samp(0) );
    _samp_v->setValue( no_samp(1) );

    _der_u->setValue( no_der(0) );
    _der_v->setValue( no_der(1) );
  }











  SurfacePlotEditWidget::SurfacePlotEditWidget( QWidget* parent ) : QWidget( parent ) {

    QHBoxLayout *layout = new QHBoxLayout( this );

    setupTreeViewEditorMargin( layout );
    layout->setSpacing(0);

    _label = new QLabel;
    layout->addWidget( _label );

    QToolButton *button = new QToolButton;
    button->setText( tr("...") );
    button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
    button->setFixedWidth(20);
    setFocusProxy(button);
    setFocusPolicy(button->focusPolicy());

    connect( button, SIGNAL(clicked()), this, SLOT(_slotButtonClicked()) );
    layout->addWidget( button );

    QToolButton *button2 = new QToolButton;
    button2->setText( tr("replot") );
    button2->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored);
    button2->setFixedWidth(45);

    connect( button2, SIGNAL(clicked()), this, SLOT(_slotReplotButtonClicked()) );
    layout->addWidget( button2 );
  };

  void SurfacePlotEditWidget::_slotButtonClicked() {

    qDebug() << "SurfacePlotEditWidget::_slotButtonClicked()";
    GMlib::Point<int,2> no_samp;
    GMlib::Point<int,2> no_der;

    SurfacePlotDialog dlg( this );
    dlg.set( _no_samp, _no_der );
    dlg.exec();
    dlg.get( no_samp, no_der );

    if( dlg.result() == QDialog::Rejected )
      return;

    if( no_samp == _no_samp && no_der == _no_der )
      return;

    setValue( no_samp, no_der );
    emit signValueChanged( _no_samp, _no_der );
  }

  void SurfacePlotEditWidget::_slotReplotButtonClicked() {

    emit signForcedReplot();
  }

  void SurfacePlotEditWidget::setNoDerivatives( const GMlib::Point<int,2>& no_der ) {

    setValue( _no_samp, no_der );
  }

  void SurfacePlotEditWidget::setNoSamples( const GMlib::Point<int,2>& no_samp ) {

    setValue( no_samp, _no_der );
  }

  void SurfacePlotEditWidget::setValue( const GMlib::Point<int,2>& no_samp, const GMlib::Point<int,2>& no_der ) {

    if( no_samp == _no_samp && no_der == _no_der )
      return;

    _no_samp = no_samp;
    _no_der = no_der;
    _label->setText(
        QString( tr("u/v: %1/%2 [%3/%4]") )
        .arg( no_samp(0) ).arg( no_samp(1) )
        .arg( no_der(0) ).arg( no_der(1) ) );
  }












  SurfacePlotEditorFactory::SurfacePlotEditorFactory( QObject* parent ) : QtAbstractEditorFactory< SurfacePlotPropertyManager >(parent) {

  }

  SurfacePlotEditorFactory::~SurfacePlotEditorFactory() {}

  void SurfacePlotEditorFactory::_slotEditorDestroyed( QObject* object ) {

    const QMap<SurfacePlotEditWidget*,QtProperty*>::iterator end = _editor_to_property.end();
    for( QMap<SurfacePlotEditWidget*,QtProperty*>::iterator eitr = _editor_to_property.begin(); eitr != end; ++eitr ) {

      if( eitr.key() == object ) {

        SurfacePlotEditWidget *editor = eitr.key();
        QtProperty *property = eitr.value();
        const QMap<QtProperty*, QList<SurfacePlotEditWidget*> >::iterator pitr = _editors.find(property);
        if( pitr != _editors.end() ) {

          pitr.value().removeAll( editor );
          if( pitr.value().empty())
            _editors.erase( pitr );
        }
        _editor_to_property.erase( eitr );
        return;
      }
    }
  }

  void SurfacePlotEditorFactory::_slotForcedReplot() {

    emit signForcedReplot();
  }

  void SurfacePlotEditorFactory::_slotSetValue( const GMlib::Point<int,2>& no_samp, const GMlib::Point<int,2>& no_der ) {

    QObject *object = sender();
    const QMap<SurfacePlotEditWidget*,QtProperty*>::ConstIterator end = _editor_to_property.constEnd();
    for( QMap<SurfacePlotEditWidget*,QtProperty*>::ConstIterator itr = _editor_to_property.constBegin(); itr != end; ++itr ) {

      if( itr.key() == object ) {

        QtProperty *property = itr.value();
        SurfacePlotPropertyManager *manager = propertyManager( property );
        if( !manager )
          return;

        manager->setNoSamples( property, no_samp );
        manager->setNoDerivatives( property, no_der );
        return;

      }
    }
  }

  void SurfacePlotEditorFactory::_slotValueChanged( QtProperty* property, const GMlib::Point<int,2>& no_samp, const GMlib::Point<int,2>& no_der ) {

    const QMap<QtProperty*, QList<SurfacePlotEditWidget*> >::iterator itr = _editors.find( property );
    if( itr == _editors.end() )
      return;
    QListIterator<SurfacePlotEditWidget*> eitr( itr.value() );

    while( eitr.hasNext() )
      eitr.next()->setValue( no_samp, no_der );

  }

  void SurfacePlotEditorFactory::connectPropertyManager( SurfacePlotPropertyManager* manager ) {

    connect(
        manager, SIGNAL(valueChanged(QtProperty*,GMlib::Point<int,2>,GMlib::Point<int,2>)),
        this, SLOT(_slotValueChanged(QtProperty*,GMlib::Point<int,2>,GMlib::Point<int,2>))
    );

    connect( this, SIGNAL(signForcedReplot()), manager, SLOT(slotForcedReplot()) );
  }

  QWidget* SurfacePlotEditorFactory::createEditor( SurfacePlotPropertyManager* manager, QtProperty* property, QWidget* parent ) {

    SurfacePlotEditWidget *editor;
    editor = new SurfacePlotEditWidget( parent );

    editor->setValue(
        manager->getNoSamples(property),
        manager->getNoDerivatives(property)
    );

    connect( editor, SIGNAL(signValueChanged(GMlib::Point<int,2>,GMlib::Point<int,2>)), this, SLOT(_slotSetValue(GMlib::Point<int,2>,GMlib::Point<int,2>)) );
    connect( editor, SIGNAL(signForcedReplot()), this, SLOT(_slotForcedReplot()) );

    _editors[property].append(editor);
    _editor_to_property[editor] = property;

    return editor;
  }

  void SurfacePlotEditorFactory::disconnectPropertyManager( SurfacePlotPropertyManager* manager ) {

    disconnect(
        manager, SIGNAL(valueChanged(QtProperty*,GMlib::Point<int,2>,GMlib::Point<int,2>)),
        this, SLOT(_slotValueChanged(QtProperty*,GMlib::Point<int,2>,GMlib::Point<int,2>))
    );

    disconnect( this, SIGNAL(signForcedReplot()), manager, SLOT(slotForcedReplot()) );
  }



} // END namespace GMQtExt
